Repository navigation
Conversation
…om:stan-dev/math into improve-logistic_lpdf-derivative-stability
Jenkins Console Log Machine informationDistributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal CPU: Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 46 bits physical, 48 bits virtual CPU(s): 80 On-line CPU(s) list: 0-79 Thread(s) per core: 2 Core(s) per socket: 20 Socket(s): 2 NUMA node(s): 2 Vendor ID: GenuineIntel CPU family: 6 Model: 85 Model name: Intel(R) Xeon(R) Gold 6248 CPU @ 2.50GHz Stepping: 7 CPU MHz: 997.515 CPU max MHz: 3900.0000 CPU min MHz: 1000.0000 BogoMIPS: 5000.00 Virtualization: VT-x L1d cache: 1.3 MiB L1i cache: 1.3 MiB L2 cache: 40 MiB L3 cache: 55 MiB NUMA node0 CPU(s): 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78 NUMA node1 CPU(s): 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79 Vulnerability Gather data sampling: Mitigation; Microcode Vulnerability Indirect target selection: Mitigation; Aligned branch/return thunks Vulnerability Itlb multihit: KVM: Mitigation: Split huge pages Vulnerability L1tf: Not affected Vulnerability Mds: Not affected Vulnerability Meltdown: Not affected Vulnerability Mmio stale data: Mitigation; Clear CPU buffers; SMT vulnerable Vulnerability Reg file data sampling: Not affected Vulnerability Retbleed: Mitigation; Enhanced IBRS Vulnerability Spec rstack overflow: Not affected Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization Vulnerability Spectre v2: Mitigation; Enhanced / Automatic IBRS; IBPB conditional; PBRSB-eIBRS SW sequence; BHI SW loop, KVM SW loop Vulnerability Srbds: Not affected Vulnerability Tsa: Not affected Vulnerability Tsx async abort: Mitigation; TSX disabled Vulnerability Vmscape: Mitigation; IBPB before exit to userspace Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cdp_l3 intel_ppin ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm mpx rdt_a avx512f avx512dq rdseed adx smap clflushopt clwb intel_pt avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req vnmi pku ospke avx512_vnni md_clear flush_l1d arch_capabilities G++: g++ (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Clang: clang version 10.0.0-4ubuntu1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin |
…om:stan-dev/math into improve-logistic_lpdf-derivative-stability
Follow-up
All three partials are now built from one Two smaller fixes in the same files:
Tests: |
Jenkins Console Log Machine informationDistributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal CPU: Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 52 bits physical, 57 bits virtual CPU(s): 192 On-line CPU(s) list: 0-191 Thread(s) per core: 2 Core(s) per socket: 48 Socket(s): 2 NUMA node(s): 2 Vendor ID: AuthenticAMD CPU family: 25 Model: 17 Model name: AMD EPYC 9474F 48-Core Processor Stepping: 1 Frequency boost: enabled CPU MHz: 1496.334 CPU max MHz: 4114.4229 CPU min MHz: 1500.0000 BogoMIPS: 7189.04 Virtualization: AMD-V L1d cache: 3 MiB L1i cache: 3 MiB L2 cache: 96 MiB L3 cache: 512 MiB NUMA node0 CPU(s): 0-47,96-143 NUMA node1 CPU(s): 48-95,144-191 Vulnerability Gather data sampling: Not affected Vulnerability Indirect target selection: Not affected Vulnerability Itlb multihit: Not affected Vulnerability L1tf: Not affected Vulnerability Mds: Not affected Vulnerability Meltdown: Not affected Vulnerability Mmio stale data: Not affected Vulnerability Reg file data sampling: Not affected Vulnerability Retbleed: Not affected Vulnerability Spec rstack overflow: Mitigation; Safe RET Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization Vulnerability Spectre v2: Mitigation; Enhanced / Automatic IBRS; IBPB conditional; STIBP always-on; PBRSB-eIBRS Not affected; BHI Not affected Vulnerability Srbds: Not affected Vulnerability Tsa: Mitigation; Clear CPU buffers Vulnerability Tsx async abort: Not affected Vulnerability Vmscape: Mitigation; IBPB before exit to userspace Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl xtopology nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd amd_ppin cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq la57 rdpid overflow_recov succor smca fsrm flush_l1d debug_swap G++: g++ (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Clang: clang version 10.0.0-4ubuntu1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin |
andrjohns
left a comment
There was a problem hiding this comment.
A couple more areas which can be moved to the log-scale for a bit more precision. I've only made the comments on the prim impls, but the same applies to all
| += -(y_dbl - mu_dbl) * sigma_inv_vec | ||
| * exp(logistic_lpdf(y_dbl, mu_dbl, sigma_dbl)) / Pn; | ||
| if constexpr (is_any_autodiff_v<T_y, T_loc, T_scale>) { | ||
| const T_partials_return deriv = inv_logit(-scaled_diff) * sigma_inv_vec; |
There was a problem hiding this comment.
This step is still subject to underflow for very large scaled_diff (i.e., very small sigma), which would round the derivative to 0.
You can keep things on the log-scale for a bit longer using log1m_inv_logit:
const T_partials_return log_sigma = log(sigma_vec);
const T_partials_return log_deriv = log1m_inv_logit(scaled_diff) - log_sigma;
const T_partials_return deriv = exp(log_deriv);
if constexpr (is_autodiff_v<T_y>) {
partials<0>(ops_partials)[n] += deriv;
}
if constexpr (is_autodiff_v<T_loc>) {
partials<1>(ops_partials)[n] -= deriv;
}
if constexpr (is_autodiff_v<T_scale>) {
partials<2>(ops_partials)[n] -= sign(scaled_diff) * exp(log(abs(scaled_diff)) + log_deriv);
}With corresponding re-ordering/changes for the LCCDF impls
There was a problem hiding this comment.
This and other issues may have been not included in the PR as some cases are quite narrow. The following is Claude assisted
This case needs σ < 2.3e-16 at z = 745, tightening to σ < 3e-40 at z = 800, and the largest gradient recoverable anywhere in the window is 8.9e-16 (beyond that 1/σ overflows). Note log_inv_logit(z) — the value the lcdf accumulates — also reaches exactly 0 at the same threshold where inv_logit(-z) does, so this recovers the slope of a numerically flat region.
But since you asked, I made a simpler faster solution. In your proposal log1m_inv_logit is a second log1p_exp on top of the one the lcdf already computes for the value, plus log(sigma) and exp. Measured on the derivative factor alone: 3.4 ns/element currently, 16.0 ns for your proposal. Behind value_of_rec(scaled_diff) > 700 it's 3.7 ns, about 10%, and that's a well-predicted branch. So the common path keeps inv_logit(-z) * inv_sigma and only the underflowing case takes the log route.
Left the scale partial as scaled_diff * deriv. Where deriv underflows we have |z| > 745, so z * deriv is larger than deriv and can't underflow if deriv didn't. The log form there costs a log, exp, sign and abs for no gain, and log(abs(0)) at z = 0 relies on sign(0) to clean up the resulting 0 * -inf.
Tests at σ = 1e-300, z = 800, one per function. Each asserts inv_logit(-z) == 0 at the test point, so they fail on the previous code.
| const T_partials_return Pn = inv_logit(scaled_diff); | ||
|
|
||
| P *= Pn; |
There was a problem hiding this comment.
This can use the same log-calculations as the lcdf (see below), accumulating on the log scale and exponentiating the result (for the derivatives as well)
There was a problem hiding this comment.
Claude assisted answer:
Measured against a long double reference, exp(Σ log inv_logit) is 749 ulp at N=100, z=−0.5 against 3.0 for the product, and 641 vs 1.0 at N=10, z=−70 — exp amplifies the summed log by |log P|·eps. It also doesn't extend the range: both forms underflow at the same points, including for a single observation at z = −745.2. inv_logit has no cancellation to avoid, unlike the difference in exp_mod_normal_cdf where the log scale genuinely buys something.
The rescaling partials *= cdf does drop the case where the cdf underflows but cdf × ∂log F/∂θ is representable. That's the same σ < 1e-16 window as the tail fix, and there the cdf returns exactly 0, so logistic_lcdf is the right function. If you want this to be changes, it needs sign tracking to exponentiate each partial separately.
| EXPECT_DOUBLE_EQ(0.0, cdf.val()); | ||
| EXPECT_DOUBLE_EQ(0.0, y.adj()); | ||
| EXPECT_DOUBLE_EQ(0.0, mu.adj()); | ||
| EXPECT_DOUBLE_EQ(0.0, sigma.adj()); |
There was a problem hiding this comment.
With the extra log-scale changes for the derivatives above, the adjoint for sigma here ends up being (barely) non-zero (~7.76e-322) instead of underflowing
Co-authored-by: Andrew Johnson <andrew.johnson@arjohnsonau.com>
|
I had tested only that OpenCL code compiles and the code matches CPU code, but it turns out the compiler can optimize things so that the accuracy drops, so I'll run additional OpenCL tests tomorrow |
Jenkins Console Log Machine informationDistributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal CPU: Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 52 bits physical, 57 bits virtual CPU(s): 192 On-line CPU(s) list: 0-191 Thread(s) per core: 2 Core(s) per socket: 48 Socket(s): 2 NUMA node(s): 2 Vendor ID: AuthenticAMD CPU family: 25 Model: 17 Model name: AMD EPYC 9474F 48-Core Processor Stepping: 1 Frequency boost: enabled CPU MHz: 1497.618 CPU max MHz: 4114.4229 CPU min MHz: 1500.0000 BogoMIPS: 7189.39 Virtualization: AMD-V L1d cache: 3 MiB L1i cache: 3 MiB L2 cache: 96 MiB L3 cache: 512 MiB NUMA node0 CPU(s): 0-47,96-143 NUMA node1 CPU(s): 48-95,144-191 Vulnerability Gather data sampling: Not affected Vulnerability Indirect target selection: Not affected Vulnerability Itlb multihit: Not affected Vulnerability L1tf: Not affected Vulnerability Mds: Not affected Vulnerability Meltdown: Not affected Vulnerability Mmio stale data: Not affected Vulnerability Old microcode: Not affected Vulnerability Reg file data sampling: Not affected Vulnerability Retbleed: Not affected Vulnerability Spec rstack overflow: Mitigation; Safe RET Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization Vulnerability Spectre v2: Mitigation; Enhanced / Automatic IBRS; IBPB conditional; STIBP always-on; PBRSB-eIBRS Not affected; BHI Not affected Vulnerability Srbds: Not affected Vulnerability Tsa: Mitigation; Clear CPU buffers Vulnerability Tsx async abort: Not affected Vulnerability Vmscape: Mitigation; IBPB before exit to userspace Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good amd_lbr_v2 nopl xtopology nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local user_shstk avx512_bf16 clzero irperf xsaveerptr rdpru wbnoinvd amd_ppin cppc arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif x2avic v_spec_ctrl vnmi avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq la57 rdpid overflow_recov succor smca fsrm flush_l1d debug_swap G++: g++ (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Clang: clang version 10.0.0-4ubuntu1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin |
|
I ran units tests only today on a Tesla V100 (OpenCL 3.0 CUDA) on the Aalto Triton cluster. All four test suites pass: 28 tests. I also compared the device results against the CPU results directly, because the tests compare them only to a tolerance. The device results and the CPU results agree to 2.0 to 2.6 ulp (which is negligible difference) |
|
CI should have a GPU available, what did you try that didn’t work as you expected? |
|
I should have written that I had not run OpenCL unit tests when I first submitted this PR, although I had checked "unit tests pass". The additional tests comparing the accuracy between CPU and GPU with millions of evaluation points are not in the unit tests, and I did run them because in erfcx PR all unit tests did pass, but comparing CPU and GPU results did reveal that OpenCL compiler had optimized one part so that the accuracy went from 5ulp to 500ulp. |
|
I just noticed that this also fixes an old issue #2827 |
Jenkins Console Log Machine informationDistributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal CPU: Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 46 bits physical, 48 bits virtual CPU(s): 80 On-line CPU(s) list: 0-79 Thread(s) per core: 2 Core(s) per socket: 20 Socket(s): 2 NUMA node(s): 2 Vendor ID: GenuineIntel CPU family: 6 Model: 85 Model name: Intel(R) Xeon(R) Gold 6248 CPU @ 2.50GHz Stepping: 7 CPU MHz: 1000.234 CPU max MHz: 3900.0000 CPU min MHz: 1000.0000 BogoMIPS: 5000.00 Virtualization: VT-x L1d cache: 1.3 MiB L1i cache: 1.3 MiB L2 cache: 40 MiB L3 cache: 55 MiB NUMA node0 CPU(s): 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78 NUMA node1 CPU(s): 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79 Vulnerability Gather data sampling: Mitigation; Microcode Vulnerability Indirect target selection: Mitigation; Aligned branch/return thunks Vulnerability Itlb multihit: KVM: Mitigation: Split huge pages Vulnerability L1tf: Not affected Vulnerability Mds: Not affected Vulnerability Meltdown: Not affected Vulnerability Mmio stale data: Mitigation; Clear CPU buffers; SMT vulnerable Vulnerability Old microcode: Not affected Vulnerability Reg file data sampling: Not affected Vulnerability Retbleed: Mitigation; Enhanced IBRS Vulnerability Spec rstack overflow: Not affected Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization Vulnerability Spectre v2: Mitigation; Enhanced / Automatic IBRS; IBPB conditional; PBRSB-eIBRS SW sequence; BHI SW loop, KVM SW loop Vulnerability Srbds: Not affected Vulnerability Tsa: Not affected Vulnerability Tsx async abort: Mitigation; TSX disabled Vulnerability Vmscape: Mitigation; IBPB before exit to userspace Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cdp_l3 intel_ppin ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm mpx rdt_a avx512f avx512dq rdseed adx smap clflushopt clwb intel_pt avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req vnmi pku ospke avx512_vnni md_clear flush_l1d arch_capabilities G++: g++ (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Clang: clang version 10.0.0-4ubuntu1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin |
|
Hi @avehtari I was working on something similar and mostly took basically the same approach here, but here are a few inconsistencies. I can send these with tests on top of this branch, or after it merges of you can copypaste the comments below to your agent. CommentsChecked at 448dcc1 with a probe (CPU, and pocl's CPU device for the OpenCL versions), mpmath as reference. These fail; all are pre-existing on develop but sit in lines this PR restructured:
Fixes: in One regression: with |
|
@jachymb thanks for the review and suggestions. Looking at those revealed additional issues, so I'm making some commits to this PR and creating one additional PR |
|
@jachymb, I could reproduce all five findings, and they are fixed now. Due to the
The scale partial when
The Tests. New tests compare against mpmath values, also for OpenCL, because, as you wrote, the OpenCL tests that compare with the CPU pass when both are wrong in the same way. To check that the new tests find the defects, I ran them with the headers of
With the current headers all tests pass: the CPU and OpenCL logistic unit tests, the existing @jachymb, please test with your Hessian/Laplace tests, too |
|
LGTM. All five findings are fixed, and I have nothing to change here. Same for #3434 I may have some related changes in backlog, but I need to look more into that myself first, and it's probably better as a follow-up separate PR after this batch gets merged. |
Jenkins Console Log Machine informationDistributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal CPU: Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 43 bits physical, 48 bits virtual CPU(s): 256 On-line CPU(s) list: 0-255 Thread(s) per core: 2 Core(s) per socket: 64 Socket(s): 2 NUMA node(s): 2 Vendor ID: AuthenticAMD CPU family: 23 Model: 49 Model name: AMD EPYC 7742 64-Core Processor Stepping: 0 Frequency boost: enabled CPU MHz: 1485.980 CPU max MHz: 3416.0681 CPU min MHz: 1500.0000 BogoMIPS: 4491.56 Virtualization: AMD-V L1d cache: 4 MiB L1i cache: 4 MiB L2 cache: 64 MiB L3 cache: 512 MiB NUMA node0 CPU(s): 0-63,128-191 NUMA node1 CPU(s): 64-127,192-255 Vulnerability Gather data sampling: Not affected Vulnerability Indirect target selection: Not affected Vulnerability Itlb multihit: Not affected Vulnerability L1tf: Not affected Vulnerability Mds: Not affected Vulnerability Meltdown: Not affected Vulnerability Mmio stale data: Not affected Vulnerability Old microcode: Not affected Vulnerability Reg file data sampling: Not affected Vulnerability Retbleed: Mitigation; untrained return thunk; SMT enabled with STIBP protection Vulnerability Spec rstack overflow: Mitigation; Safe RET Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization Vulnerability Spectre v2: Mitigation; Retpolines; IBPB conditional; STIBP always-on; RSB filling; PBRSB-eIBRS Not affected; BHI Not affected Vulnerability Srbds: Not affected Vulnerability Tsa: Not affected Vulnerability Tsx async abort: Not affected Vulnerability Vmscape: Mitigation; IBPB before exit to userspace Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 cqm rdt_a rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local clzero irperf xsaveerptr rdpru wbnoinvd amd_ppin arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif v_spec_ctrl umip rdpid overflow_recov succor smca sev sev_es G++: g++ (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Clang: clang version 10.0.0-4ubuntu1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin |
EDIT: 2026-10-22 rewrote the PR text to describe the current status (including changes after review comments)
Fixes #2827
This PR is based on #3452 and must be merged after it. The second derivative of
logistic_lpdfin nested forward mode needs the forward-modetanhfix in #3452.This PR was assisted by Sol and Claude.
Summary
This PR changes the values and the derivatives of
logistic_lpdf,logistic_cdf,logistic_lcdfandlogistic_lccdf, on the CPU and in OpenCL. Each function now computes the standardized differencez = (y - mu) / sigmaonce and builds all three partials from one derivative factor.logistic_lpdfOn develop, the location derivative is
(1 - 2 exp(mu / sigma) / (exp(mu / sigma) + exp(y / sigma))) / sigma. It exponentiatesmu / sigmaandy / sigmaseparately. Whenmu / sigma > log(DBL_MAX / 2), the result can be infinite orNaN, although the log density and the exact derivative are finite. For example,y = 711,mu = 710,sigma = 1gives aNaNlocation derivative instead oftanh(0.5).The denominator overflows before
2 * exp(m)does, withm = mu / sigma, so the derivative is wrong before it becomesNaN. Form = log(DBL_MAX / 2) - 1e-6andz = 0.1, develop gives1.0; the exact value istanh(0.05) = 0.04995837495788.Below the overflow threshold the error is usually small. Above it, the derivative first has a large finite error (the denominator overflows) and then becomes
-InforNaN(the numerator overflows too). The change is abrupt, so an HMC trajectory that enters this region gets a wrong gradient without warning.The
ypartial on develop is(2 / (1 + exp(z)) - 1) / sigma. It has no overflow problem, but it loses all relative precision neary == mu:z2 / (1 + exp(z)) - 1-tanh(z / 2)This PR computes
tanh(z / 2) / sigmaonce and builds they,muandsigmapartials from it. The factor depends only onz, its absolute value is at most1 / sigma, andd/dy == -d/dmuholds exactly. The OpenCL kernel needs oneexpless.The value is now
-|z| - 2 log1p_exp(-|z|). The density is symmetric inz, and the develop form-z - 2 log1p_exp(-z)givesinf - inf = NaNwhenzoverflows to-inf, for examplelogistic_lpdf(-1e308 | 1e308, 1). The new form gives-inf.logistic_lccdfFor
logistic_lccdf(z | 0, 1):z-Inf-InfThe value and the gradient are already wrong at
z = 30, before the result saturates, and values ofznear 30 can occur in practice.This PR computes the value as
log1m_inv_logit(z)and the gradients frominv_logit(z) / sigma.The PR also changes the handling of infinite
y:y == -infmakes the whole call return 0 with zero partials. Fory = {-inf, 0}the exact value islog(0.5). Now an element withy == -infadds 0 to the value and to the partials, and the other elements count as usual. On OpenCL,select(y == NEGATIVE_INFTY, 0, ...)replaces the early return.y == +infis inside the accumulation loop, so the returned-infkeeps the partials of the elements before it. Now the scan for+infruns before the loop.y = {-inf, +inf}gives-inf.logistic_lcdfFor
logistic_lcdf(z | 0, 1):z-InfOn develop, the gradient stays exactly
1whileexp(z)is subnormal and becomesNaNwhenexp(z)underflows to 0. Of the defects in this PR, this one is the least likely to occur, because it needszbelow about -730.This PR computes the value as
log_inv_logit(z)and the gradients frominv_logit(-z) / sigma.logistic_cdfOn develop, the partials are
exp(logistic_lpdf(y, mu, sigma)) / Pn. This quotient is0 / 0whenPnunderflows:NaNatz = -746on the CPU, and fromz ≈ -709in OpenCL, whereexp(-z)overflows first. This PR accumulatesinv_logit(-z) / sigma, the derivative oflog F, and multiplies the partials by the product of the cdfs at the end.Changes common to the three cdf functions
The derivative factor
inv_logit(-z) / sigma(inv_logit(z) / sigmainlccdf) underflows to 0 forzabove about 745, although the exact partial is representable whensigmais small (below 2.3e-16 atz = 745). Forz > 700(z < -700inlccdf), the factor is nowexp(log1m_inv_logit(z) - log(sigma)). The common path keeps the cheap form; the branch makes the factor about 10 % slower (3.7 ns against 3.4 ns per element). The OpenCL versions use a new device functionlogistic_tail_deriv.When
zoverflows with finiteyandmu(for examplelcdf(1e10 | 0, 1e-300)), the scale partial on develop isz * deriv = inf * 0 = NaN. This PR sets it to 0 on the side where the factor is 0:z == +infinlcdf,z == -infinlccdf, both sides incdf. On the other side oflcdfandlccdfthe exact partial overflows, so+infis the correctly rounded result. For example,d/dsigmaoflcdf(-1e10 | 0, 1e-300)is-z (1 - F) / sigma, about1e310 * 1e300. Incdfboth sides give 0, becauseP = 0whenz = -inf.On develop, the OpenCL
logistic_lcdfandlogistic_cdfalso give0 * INFTY = NaNfor the scale partial aty == +inf, where the CPU version skips the element. They now give 0, the same as the CPU.Not changed
The review suggested to accumulate
logistic_cdfon the log scale and exponentiate the sum. I did not do this. Against along doublereference,exp(Σ log inv_logit)has an error of 749 ulp atN = 100,z = -0.5, against 3.0 ulp for the product, and 641 against 1.0 ulp atN = 10,z = -70. Both forms underflow at the same points. Where the product underflows,logistic_lcdfis the correct function to use.Tests
test/unit/math/rev/prob/logistic_{lpdf,cdf,lcdf,lccdf}_test.cppwith 25 tests. They compare with known values and with mpmath reference values. The cases includemu != 0andsigma != 1(so thatsigma == 1does not hide an error in the scale partial),z = 30forlccdf,y ≈ muwithy.adj() == -mu.adj()bit for bit, a vectorizedmu, the tail branch atsigma = 1e-300,z = 800, infinitey, and overflowedz.test/unit/math/mix/prob/logistic_lpdf_test.cpp: the first and the second derivative infvar<fvar<double>>andfvar<var>atz = 1, -20, 40, 100and atsigma = 2.test/unit/math/opencl/rev/logistic_*_test.cpp(43 in total). The new tests compare with mpmath reference values, not only with the CPU, because a comparison with the CPU passes when both are wrong in the same way.448dcc178c; the other 3 check behavior that did not change. With the same headers, the 5 new OpenCL reference tests fail, and so does the existingopencl_matches_cpu_small_y_neg_inf, because the CPUlccdfnow differs from the old OpenCLlccdfaty = -inf. The second-derivative test fails with the developfwd/fun/tanh.hppand passes with Accurate forward-mode tanh derivative and correlation transforms #3452.primlogistic tests, and the generatedtest/prob/logistictests (23 173 cases). Tested with develop43bc5684a3plus this PR (g++ 14.3), and with Accurate forward-mode tanh derivative and correlation transforms #3452 plus this PR (g++ 11.5). OpenCL tests ran on a Tesla V100 (OpenCL 3.0 CUDA).448dcc178cI also compared the GPU results with the CPU results on a dense sweep, because the unit tests compare them only to a tolerance. They agree to 2.0 to 2.6 ulp.The review findings and the fixes are in this comment.
Side Effects
logistic_cdf,logistic_lcdfandlogistic_lccdfno longer includelogistic_lpdf.hpp.logistic_tail_deriv, registered inelt_function_cl.hpp.logistic_cdf,logistic_lcdfandlogistic_lccdf, because the PR does the simplification and the log-scale computation that the comment asks for:Release notes
logistic_lpdf,logistic_cdf,logistic_lcdfandlogistic_lccdf.Checklist
Copyright holder: Aki Vehtari
The copyright holder is typically you or your assignee, such as a university or company. By submitting this pull request, the copyright holder is agreeing to the license the submitted work under the following licenses:
- Code: BSD 3-clause (https://opensource.org/licenses/BSD-3-Clause)
- Documentation: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
the basic tests are passing
./runTests.py test/unit)make test-headers)make test-math-dependencies)make doxygen)make cpplint)the code is written in idiomatic C++ and changes are documented in the doxygen
the new changes are tested